import type { ElementCheckResult, ShadowDOMCheckOptions } from './utils/ui/shadow_style'
import { logger } from '../types/constants'
import { SYMBOLS } from '../types/interfaces'
import { TimerManager } from './timer_manager'
import { ToolKit } from './utils/toolkit'

interface PollingResult {
  elapsedTime: number // 总耗时(ms)
  pollCount: number // 轮询次数
}

export interface PollingBaseOptions {
  interval?: number // 轮询间隔(ms)，默认 300 ms
  timeout?: number // 超时时间(ms)，默认 10 秒
  timeoutLogLevel?: 'info' | 'warning' // 超时日志级别
  onTimeout?: (stats: PollingResult) => void // 超时回调（带统计信息）
  shadowDOM?: ShadowDOMCheckOptions // Shadow DOM 检查配置
}

/**
 * 轮询元素检查结果
 */
export interface PollingElementCheckResult {
  elements: Element[]
  shadowResults: ElementCheckResult[]
  allFound: boolean
}

interface PollingOptions<T = boolean> extends PollingBaseOptions {
  checkFn: () => T | Promise<T> // 检查函数
  condition?: (result: T) => boolean // 成功条件判断
  onSuccess?: (result: T, stats: PollingResult) => void // 成功回调（带统计信息）
  onError?: (error: Error, stats: PollingResult) => void // 错误回调（带统计信息）
}

export interface IntervalRecord {
  startTime: number // 轮询间隔开始时间
  stopped: boolean // 是否已停止
  count: number // 轮询次数
}

export class PollingHandler {
  readonly id: string = 'polling-handler'
  readonly name: string = 'PollingHandler'
  private static instance: PollingHandler | null = null
  private readonly timerManager: TimerManager = TimerManager.getInstance()
  private intervalRecords: Map<string, IntervalRecord> = new Map()
  private timeoutIds: Map<string, string> = new Map()

  /**
   * 获取单例实例
   */
  public static getInstance(): PollingHandler {
    if (!PollingHandler.instance) {
      PollingHandler.instance = new PollingHandler()
    }

    return PollingHandler.instance
  }

  get api(): {
    awaitElement: (selector: string | string[], handler: () => void, options?: PollingBaseOptions) => void
    awaitCondition: (condition: () => boolean, handler: () => void, options?: PollingBaseOptions) => void
    awaitElementWithResult: <T>(selector: string | string[], handler: () => T, options?: PollingBaseOptions) => Promise<T | null>
    awaitConditionWithResult: <T>(condition: () => boolean, handler: () => T, options?: PollingBaseOptions) => Promise<T | null>
  } {
    return {
      awaitElement: this.awaitElement.bind(this),
      awaitCondition: this.awaitCondition.bind(this),
      awaitElementWithResult: this.awaitElementWithResult.bind(this),
      awaitConditionWithResult: this.awaitConditionWithResult.bind(this),
    }
  }

  /**
   * 等待元素出现
   */
  private awaitElement(
    selector: string | string[],
    handler: () => void,
    options?: PollingBaseOptions,
  ): void {
    const { interval = 200, timeout = 5000, timeoutLogLevel = 'info', onTimeout, shadowDOM } = options || {}
    const selectors = typeof selector === 'string' ? [selector] : selector

    this.start(
      {
        checkFn: () => this.isElementsAvailable(shadowDOM, selectors),
        interval,
        timeout,
        onSuccess: (_found, stats) => {
          logger.info(`[${this.name}] 找到目标元素，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`, selector)

          handler()
        },
        onTimeout: (stats) => {
          if (timeoutLogLevel === 'info') {
            logger.notice(`[${this.name}] ${SYMBOLS.STATUS.WARNING} 未找到目标元素，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`, selector)
          }
          else {
            logger.warn(`[${this.name}] 未找到目标元素，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`, selector)
          }
          onTimeout?.(stats)
        },
      },
    )
  }

  /**
   * 等待元素出现（支持返回结果）
   */
  private async awaitElementWithResult<T>(
    selector: string | string[],
    handler: () => T,
    options?: PollingBaseOptions,
  ): Promise<T | null> {
    const { interval = 200, timeout = 5000, timeoutLogLevel = 'info', onTimeout, shadowDOM } = options || {}
    const selectors = typeof selector === 'string' ? [selector] : selector

    return new Promise<T | null>((resolve) => {
      const pollingResult = this.start({
        checkFn: () => this.isElementsAvailable(shadowDOM, selectors),
        interval,
        timeout,
        onSuccess: (_found, stats) => {
          logger.info(`[${this.name}] 找到目标元素，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`, selector)

          try {
            const result = handler()
            resolve(result)
          }
          catch (error) {
            logger.error(`[${this.name}] 元素处理函数执行失败:`, error)
            resolve(null)
          }
        },
        onTimeout: (stats) => {
          if (timeoutLogLevel === 'info') {
            logger.notice(`[${this.name}] ${SYMBOLS.STATUS.WARNING} 未找到目标元素，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`, selector)
          }
          else {
            logger.warn(`[${this.name}] 未找到目标元素，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`, selector)
          }
          onTimeout?.(stats)
          resolve(null)
        },
        onError: (error, stats) => {
          logger.error(`[${this.name}] 轮询过程中出错，耗时 ${stats.elapsedTime}ms:`, error)
          resolve(null)
        },
      })

      // 返回停止函数，允许外部提前终止
      return {
        stop: () => pollingResult.stop(pollingResult.intervalId),
      }
    })
  }

  /**
   * 等待条件满足
   */
  private awaitCondition(condition: () => boolean, handler: () => void, options?: PollingBaseOptions): void {
    const { interval = 200, timeout = 5000, timeoutLogLevel = 'info' } = options || {}

    this.start({
      checkFn: condition,
      interval,
      timeout,
      onSuccess: (_result, stats) => {
        logger.info(`[${this.name}] 条件满足，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`)
        handler()
      },
      onTimeout: (stats) => {
        if (timeoutLogLevel === 'info') {
          logger.notice(`[${this.name}] ${SYMBOLS.STATUS.WARNING} 条件未满足，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`)
        }
        else {
          logger.warn(`[${this.name}] 条件未满足，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`)
        }
      },
    })
  }

  /**
   * 等待条件满足（支持返回结果）
   */
  private async awaitConditionWithResult<T>(
    condition: () => boolean,
    handler: () => T,
    options?: PollingBaseOptions,
  ): Promise<T | null> {
    const { interval = 200, timeout = 5000, timeoutLogLevel = 'info', onTimeout } = options || {}

    return new Promise<T | null>((resolve) => {
      const pollingResult = this.start({
        checkFn: condition,
        interval,
        timeout,
        onSuccess: (_result, stats) => {
          logger.info(`[${this.name}] 条件满足，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`)

          try {
            const result = handler()
            resolve(result)
          }
          catch (error) {
            logger.error(`[${this.name}] 条件处理函数执行失败:`, error)
            resolve(null)
          }
        },
        onTimeout: (stats) => {
          if (timeoutLogLevel === 'info') {
            logger.notice(`[${this.name}] ${SYMBOLS.STATUS.WARNING} 条件未满足，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`)
          }
          else {
            logger.warn(`[${this.name}] 条件未满足，耗时 ${stats.elapsedTime}ms，轮询 ${stats.pollCount} 次`)
          }
          onTimeout?.(stats)
          resolve(null)
        },
        onError: (error, stats) => {
          logger.error(`[${this.name}] 条件检查过程中出错，耗时 ${stats.elapsedTime}ms:`, error)
          resolve(null)
        },
      })

      // 返回停止函数
      return {
        stop: () => pollingResult.stop(pollingResult.intervalId),
      }
    })
  }

  start<T>(options: PollingOptions<T>): {
    intervalId: string
    stop: (intervalId: string) => void
  } {
    const {
      interval = 300,
      timeout = 10000,
      checkFn,
      condition = (result: any) => !!result,
      onSuccess,
      onTimeout,
      onError,
    } = options

    const intervalId = this.generateId('interval')
    const timeoutId = this.generateId('timeout')

    // 记录 ID
    this.intervalRecords.set(intervalId, { startTime: Date.now(), stopped: false, count: 0 })
    this.timeoutIds.set(intervalId, timeoutId)

    const cleanup = (intervalId: string) => {
      this.stop(intervalId)
    }

    const executeCheck = async (intervalId: string) => {
      try {
        this.updateIntervalRecord(intervalId, { incrementCount: true })

        const result = await checkFn()

        if (condition(result) && this.isActive(intervalId)) {
          onSuccess?.(result, this.getStats(intervalId))

          cleanup(intervalId)
        }
      }
      catch (error: any) {
        onError?.(error as Error, this.getStats(intervalId))
        logger.error(`[${this.name}] 检测过程中出错，耗时 ${this.getStats(intervalId).elapsedTime}ms`, error)

        cleanup(intervalId)
      }
    }

    // 设置轮询
    this.timerManager.createTimerTask({
      id: intervalId,
      interval,
      immediate: true,
      callback: () => executeCheck(intervalId),
    })

    // 设置超时
    this.timerManager.createTimerTask({
      id: timeoutId,
      delay: timeout,
      callback: () => {
        if (!this.isActive(intervalId))
          return

        onTimeout?.(this.getStats(intervalId))
        cleanup(intervalId)
      },
    })

    return {
      intervalId,
      stop: cleanup,
    }
  }

  /**
   * 生成轮询间隔 ID
   */
  generateId(type: 'interval' | 'timeout'): string {
    return `polling-${type}-${ToolKit.string.generateUniqueId()}`
  }

  /**
   * 获取轮询间隔的开始时间
   */
  getStats(intervalId: string): PollingResult {
    const record = this.intervalRecords.get(intervalId)

    return {
      elapsedTime: Date.now() - (record?.startTime || Date.now()),
      pollCount: record?.count || 0,
    }
  }

  /**
   * 更新轮询间隔
   */
  updateIntervalRecord(intervalId: string, options?: {
    stopped?: boolean
    incrementCount?: boolean
  }): void {
    const { stopped = false, incrementCount = false } = options || {}

    // 获取现有记录或创建新记录
    const oldRecord: IntervalRecord = this.intervalRecords.get(intervalId) || {
      startTime: Date.now(),
      stopped: false,
      count: 0,
    }

    const newRecord: IntervalRecord = {
      startTime: oldRecord.startTime,
      stopped,
      count: incrementCount ? oldRecord.count + 1 : oldRecord.count,
    }

    this.intervalRecords.set(intervalId, newRecord)
  }

  stop(intervalId: string): void {
    this.intervalRecords.delete(intervalId)
    this.timerManager.clearInterval(intervalId)
    this.timeoutIds.delete(intervalId)
  }

  isActive(intervalId: string): boolean {
    return this.intervalRecords.get(intervalId)?.stopped === false
  }

  /**
   * 所有元素是否存在（支持 Shadow DOM 检查）
   */
  private isElementsAvailable(shadowDOM: ShadowDOMCheckOptions | undefined, selectors: string[]): boolean {
    const checkShadowDOM = shadowDOM?.enabled ?? false
    const result = this.checkElements(shadowDOM, selectors)

    // 基础条件：所有选择器都找到元素
    if (!result.allFound)
      return false

    // 如果启用了 Shadow DOM 检查
    if (checkShadowDOM) {
      const { requireShadowRoot = true, mode = 'open', checkChildrenShadowDOM = false, checkChildrenExist = false, customCheck } = shadowDOM || {}

      // 检查所有元素是否满足 Shadow DOM 条件
      return result.shadowResults.every((shadowResult) => {
        const { element, hasShadowRoot, shadowRootMode, childrenWithShadow, childrenIsExists } = shadowResult
        if (requireShadowRoot && !hasShadowRoot)
          return false

        // 检查 ShadowRoot 模式
        if (shadowRootMode) {
          if (mode === 'open' && shadowRootMode !== 'open')
            return false
          if (mode === 'closed' && shadowRootMode !== 'closed')
            return false
          // 'both' 模式接受任何 ShadowRoot
        }

        // 检查是否存在子元素的 Shadow DOM
        if (checkChildrenShadowDOM && (!childrenWithShadow || childrenWithShadow.length === 0))
          return false

        // 检查是否存在子元素
        if (checkChildrenExist && !childrenIsExists)
          return false

        // 自定义检查
        if (customCheck)
          return customCheck(element)

        return true
      })
    }

    return true
  }

  /**
   * 检查元素是否存在
   */
  private checkElements(shadowDOM: ShadowDOMCheckOptions | undefined, selectors: string[]): PollingElementCheckResult {
    const checkShadowDOM = shadowDOM?.enabled ?? false

    // 检查元素是否存在
    const allElements: Element[] = []
    const shadowResults: ElementCheckResult[] = []

    for (const sel of selectors) {
      const elements = Array.from($(sel)) as Element[]
      if (elements.length > 0) {
        allElements.push(...elements)

        // 如果需要检查 Shadow DOM
        if (checkShadowDOM) {
          elements.forEach((element) => {
            const shadowResult = ToolKit.ui.shadow.checkElementShadowDOM(element, shadowDOM)
            shadowResults.push(shadowResult)
          })
        }
      }
    }

    return {
      elements: allElements,
      shadowResults,
      allFound: allElements.length >= selectors.length,
    }
  }

  /**
   * 清理所有轮询任务
   */
  cleanupAll(): void {
    // 清理所有定时器
    for (const intervalId of this.intervalRecords.keys()) {
      this.timerManager.clearInterval(intervalId)
    }
    for (const timeoutId of this.timeoutIds.values()) {
      this.timerManager.clearTimeout(timeoutId)
    }

    // 清空记录
    this.intervalRecords.clear()
    this.timeoutIds.clear()
  }
}
